import {Color} from '../color';
import {Plugin} from '../extend';
import {AnyColor} from '../type';
import {floor, round} from '../helper';
import {getContrast} from '../get/get-contrast';
import {getLuminance} from '../get/get-luminance';

// https://webaim.org/resources/contrastchecker/
interface ReadabilityOption {
    level?: 'AA' | 'AAA';
    size?: 'normal' | 'large';
}

declare module '../color' {
    interface Color {
        /**
         * Returns the relative luminance of a color,
         * normalized to 0 for darkest black and 1 for lightest white.
         * https://www.w3.org/TR/WCAG20/#relativeluminancedef
         * https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance
         */
        luminance(): number;

        /**
         * Calculates a contrast ratio for a color pair.
         * This luminance difference is expressed as a ratio ranging
         * from 1 (e.g. white on white) to 21 (e.g., black on a white).
         * WCAG requires a ratio of at least 4.5 for normal text and 3 for large text.
         * https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html
         * https://webaim.org/articles/contrast/
         */
        contrast(c?: AnyColor | Color): number;

        /**
         * Checks that a background and text color pair conforms to WCAG 2.0 requirements.
         * https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html
         */
        isReadable(c?: AnyColor | Color, option?: ReadabilityOption): boolean;
    }
}

/**
 * A plugin adding accessibility and color contrast utilities.
 * Follows Web Content Accessibility Guidelines 2.0.
 * https://www.w3.org/TR/WCAG20/
 */
const a11yPlugin: Plugin = (ColorClass: typeof Color): void => {
    /**
     * Returns WCAG text color contrast requirement.
     * Read explanation here https://webaim.org/resources/contrastchecker/
     */
    const getMinimalContrast = ({level = 'AA', size = 'normal'}: ReadabilityOption) => {
        if (level === 'AAA' && size === 'normal') return 7;
        if (level === 'AA' && size === 'large') return 3;
        return 4.5;
    };

    ColorClass.prototype.luminance = function() {
        return round(getLuminance(this.rgba), 2);
    };

    ColorClass.prototype.contrast = function(c: AnyColor | Color = '#FFF') {
        const instance2 = c instanceof ColorClass ? c : new ColorClass(c);
        return floor(getContrast(this.rgba, instance2.toRgb()), 2);
    };

    ColorClass.prototype.isReadable = function(c: AnyColor | Color = '#FFF', option = {}) {
        return this.contrast(c) >= getMinimalContrast(option);
    };
};

export default a11yPlugin;
